package com.py.framework.core.http;

import java.nio.charset.CodingErrorAction;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;

import javax.net.ssl.SSLContext;

import org.apache.http.Consts;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.Lookup;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.impl.auth.BasicSchemeFactory;
import org.apache.http.impl.auth.DigestSchemeFactory;
import org.apache.http.impl.auth.KerberosSchemeFactory;
import org.apache.http.impl.auth.NTLMSchemeFactory;
import org.apache.http.impl.auth.SPNegoSchemeFactory;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.nio.conn.NoopIOSessionStrategy;
import org.apache.http.nio.conn.SchemeIOSessionStrategy;
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.nio.reactor.IOReactorException;
import org.apache.http.ssl.SSLContexts;

import com.py.framework.core.log.ExceptionLogger;  
  
/**
 * 异步的HTTP请求对象，可设置代理   
 * @author Leegern
 * @date   2018年4月28日
 */
public class HttpAsyncAgent {  
  
    private static int socketTimeout = 8000;  // 设置等待数据超时时间5秒钟 根据业务调整  
  
    private static int connectTimeout = 6000; // 连接超时  
  
    private static int poolSize = 3500;       // 连接池最大连接数  
  
    private static int maxPerRoute = 1500;    // 每个主机的并发最多只有1500  
  
    // http代理相关参数  
    private String host = "";  
    private int port = 0;  
    private String username = "";  
    private String password = "";  
  
    private static HttpAsyncAgent instance;
    
    // 异步httpclient  
    private CloseableHttpAsyncClient asyncHttpClient;  
  
    // 异步加代理的httpclient  
    private CloseableHttpAsyncClient proxyAsyncHttpClient;  
    
    /**
     * 构造器
     */
    public HttpAsyncAgent() {  
        try {  
            this.asyncHttpClient = createAsyncClient(false);  
            this.proxyAsyncHttpClient = createAsyncClient(true);  
        } catch (Exception e) {  
            ExceptionLogger.error(e);
        }  
    }  
    
    /**
     * 获取HttpAsyncAgent实例
     * @return
     */
    public static synchronized HttpAsyncAgent getInstance() {
    	if (null == instance) {
    		instance = new HttpAsyncAgent();
    	}
    	return instance;
    }
    
    /**
     * 创建异步Http对象
     * @param proxy
     * @return
     * @throws KeyManagementException
     * @throws UnrecoverableKeyException
     * @throws NoSuchAlgorithmException
     * @throws KeyStoreException
     * @throws MalformedChallengeException
     * @throws IOReactorException
     */
    public CloseableHttpAsyncClient createAsyncClient(boolean proxy)  
            throws KeyManagementException, UnrecoverableKeyException,  
            NoSuchAlgorithmException, KeyStoreException,  
            MalformedChallengeException, IOReactorException {  
  
        RequestConfig requestConfig = RequestConfig.custom()  
                .setConnectTimeout(connectTimeout)  
                .setSocketTimeout(socketTimeout).build();  
  
        SSLContext sslcontext = SSLContexts.createDefault();
  
        UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(  
                username, password);  
  
        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();  
        credentialsProvider.setCredentials(AuthScope.ANY, credentials);  
  
        // 设置协议http和https对应的处理socket链接工厂的对象  
        Registry<SchemeIOSessionStrategy> sessionStrategyRegistry = RegistryBuilder  
                .<SchemeIOSessionStrategy> create()  
                .register("http", NoopIOSessionStrategy.INSTANCE)  
                .register("https", new SSLIOSessionStrategy(sslcontext))  
                .build();  
  
        // 配置io线程  
        IOReactorConfig ioReactorConfig = IOReactorConfig.custom()  
                .setIoThreadCount(Runtime.getRuntime().availableProcessors() + 1)  
                .build();  
        // 设置连接池大小  
        ConnectingIOReactor ioReactor;  
        ioReactor = new DefaultConnectingIOReactor(ioReactorConfig);  
        PoolingNHttpClientConnectionManager conMgr = new PoolingNHttpClientConnectionManager(  
                ioReactor, null, sessionStrategyRegistry, null);  
  
        if (poolSize > 0) {  
            conMgr.setMaxTotal(poolSize);  
        }  
  
        if (maxPerRoute > 0) {  
            conMgr.setDefaultMaxPerRoute(maxPerRoute);  
        } else {  
            conMgr.setDefaultMaxPerRoute(10);  
        }  
  
        ConnectionConfig connectionConfig = ConnectionConfig.custom()  
                .setMalformedInputAction(CodingErrorAction.IGNORE)  
                .setUnmappableInputAction(CodingErrorAction.IGNORE)  
                .setCharset(Consts.UTF_8).build();  
  
        Lookup<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder  
                .<AuthSchemeProvider> create()  
                .register(AuthSchemes.BASIC, new BasicSchemeFactory())  
                .register(AuthSchemes.DIGEST, new DigestSchemeFactory())  
                .register(AuthSchemes.NTLM, new NTLMSchemeFactory())  
                .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())  
                .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory())  
                .build();  
        conMgr.setDefaultConnectionConfig(connectionConfig);  
  
        if (proxy) {  
            return HttpAsyncClients.custom().setConnectionManager(conMgr)  
                    .setDefaultCredentialsProvider(credentialsProvider)  
                    .setDefaultAuthSchemeRegistry(authSchemeRegistry)  
                    .setProxy(new HttpHost(host, port))  
                    .setDefaultCookieStore(new BasicCookieStore())  
                    .setDefaultRequestConfig(requestConfig).build();  
        } else {  
            return HttpAsyncClients.custom().setConnectionManager(conMgr)  
                    .setDefaultCredentialsProvider(credentialsProvider)  
                    .setDefaultAuthSchemeRegistry(authSchemeRegistry)  
                    .setDefaultCookieStore(new BasicCookieStore()).build();  
        }  
    }  
    
    /**
     * 获取异步Http实例
     * @return
     */
    public CloseableHttpAsyncClient getAsyncHttpClient() {  
        return asyncHttpClient;  
    }  
    
    /**
     * 获取异步代理Http实例
     * @return
     */
    public CloseableHttpAsyncClient getProxyAsyncHttpClient() {  
        return proxyAsyncHttpClient;  
    }  
  
}